const fs = require('fs')
const path = require('path')

/**
 * 2020-10-27 sheyifan Issue: custom type
 * @typedef {{
 *		"待办": string,
 *		"保密": boolean,
 *		"事项说明": string,
 *		"标签": Array<string>,
 *		"风险": string,
 *		"完成状态": number,
 *		"完成说明": string,
 *		"资源求助": string
 * }} IssueItems - content of single issue
 * @typedef {{
 *		"日": string,
 *		"继承": boolean,
 *		"T0事项": Array<IssueItems>,
 *		"T1事项": Array<IssueItems>,
 *		"T2事项": Array<IssueItems>,
 *      "预约": {
 *          "T0事项": Array<IssueItems>,
 *		    "T1事项": Array<IssueItems>,
 *		    "T2事项": Array<IssueItems>,
 *      }
 * }} DayIssue - content of issues in single day
 * @typedef {{
 *	   "T0事项": IssueItems,
 *	   "T1事项": IssueItems,
 *	   "T2事项": IssueItems,
 *     "预约": {
 *         "T0事项": IssueItems,
 *		   "T1事项": IssueItems,
 *		   "T2事项": IssueItems,
 *      }
 * }} FilterCondition
 * @typedef {{
 *     today: {
 *         T0: FinishData,
 *         T1: FinishData,
 *         T2: FinishData
 *     },
 *     days7: {
 *         data: Array<FinishData>
 *     },
 *     month6: {
 *         data: Array<FinishData>
 *     },
 *     thisWeek: {
 *         data: Array<number>
 *     }
 * }} RatioData
 * @typedef {{
 *     finish: number,
 *     undone: number
 * }} FinishData - state of issues
 */

const { fstat } = require("fs")

/**
 * 2020-10-25 22:46 sheyifan
 * TODO: fill data to get into table
 * @param {DayIssue} data 
 */
function getRadio(data) {
	let t0 = data.T0事项
	let t0Count = 0
	let t0Accomplished = 0
	if(t0 == undefined) {
		t0Count = 0
	}
	else {
		t0Count = t0.length
	}
	// Accomplished radio of T0 issues
	let t0Ratio = 0
	let t0RatioStr = ""
	if (t0Count === 0 || data.T0事项 == undefined) {
		t0Ratio = 0
		t0RatioStr = "0/0"
	}
	else {
		// 2020-10-28 sheyifan Bug fix: handle undefined items
		for (let issue of (data.T0事项 !== undefined)?data.T0事项:[]) {
			if (issue.完成状态 === 1) {
				t0Accomplished = t0Accomplished + 1
			}
		}
		t0Ratio = t0Accomplished / t0Count
		t0RatioStr = t0Accomplished.toString() + "/" + t0Count.toString()
	}

	let t1 = data.T1事项
	let t1Count = 0
	let t1Accomplished = 0
	if(t1 == undefined) {
		t1Count = 0
	}
	else {
		t1Count = t1.length
	}
	// Accomplished radio of T1 issues
	let t1Ratio = 0
	let t1RatioStr = ""
	if (t1Count === 0 || data.T1事项 == undefined) {
		t1Ratio = 0
		t1RatioStr = "0/0"
	}
	else {
		// 2020-10-28 sheyifan Bug fix: handle undefined items
		for (let issue of (data.T1事项 !== undefined)?data.T1事项:[]) {
			if (issue.完成状态 === 1) {
				t1Accomplished = t1Accomplished + 1
			}
		}
		t1Ratio = t1Accomplished / t1Count
		t1RatioStr = t1Accomplished.toString() + "/" + t1Count.toString()
	}

	let t2 = data.T2事项
	let t2Count = 0
	let t2Accomplished = 0
	if(t2 == undefined) {
		t2Count = 0
	}
	else {
		t2Count = t2.length
	}
	// Accomplished radio of T2 issues
	let t2Ratio = 0
	let t2RatioStr = ""
	if (t2Count === 0 || data.T2事项 == undefined) {
		t2Ratio = 0
		t2RatioStr = "0/0"
	}
	else {
		// 2020-10-28 sheyifan Bug fix: handle undefined items
		for (let issue of (data.T2事项 !== undefined)?data.T2事项:[]) {
			if (issue.完成状态 === 1) {
				t2Accomplished = t2Accomplished + 1
			}
		}
		t2Ratio = t2Accomplished / t2Count
		t2RatioStr = t2Accomplished.toString() + "/" + t2Count.toString()
	}

	let count = t0Count + t1Count + t2Count
	let accomplished = t0Accomplished + t1Accomplished + t2Accomplished
	ratio = count / accomplished
	ratioStr = accomplished.toString() + "/" + count.toString()

	return {
		T0事项完成率: {
			str: t0RatioStr,
			num: t0Ratio
		},
		T1事项完成率: {
			str: t1RatioStr,
			num: t1Ratio
		},
		T2事项完成率: {
			str: t2RatioStr,
			num: t2Ratio
		},
		总完成率: {
			str: ratioStr,
			num: ratio
		}
	}
}

// line structure of table
tableStruc = {
	优先级: 0,
	待办事项: "",
	代办说明: "",
	标签: [],
	风险: "",
	资源求助: "",
	完成状态: "",
	完成说明: ""
}

/**
 * 2020-11-04 sheyifan Issue: clear table before re-add rows
 * @param {HTMLTableElement} table 
 */
function clear(table) {
	let rows = table.querySelectorAll("tbody tr")
	// 2020-11-04 sheyifan Attention: donnot need to remove the first invisible row,
	// which used as pattern of new row
	for (let i = 1 ; i < rows.length ; i++) {
		rows.item(i).remove()
	}
}

/**
 * 2020-10-25 22:30 sheyifan Issue: create function
 * 2020-10-27 20:49 sheyifan Issue: transform common comment to JSDoc for type annotation
 * TODO: fill data to get into table
 * @param {DayIssue} data - Json object shown in 'data/log/*.json'
 * @param {HTMLTableElement} table: where undone data should be filled in 
 */
function fillTable(data, table) {
	if (data == undefined || data == null) {
		console.log("No data can be filled into table")
		return
	}

	clear(table)

	let tr = table.querySelector('tbody tr')
	// 2020-10-26 03:49 sheyifan Issue: index flag indicating index of issues
	let issueIndex = 0
	// 2020-10-28 sheyifan Bug fix: handle undefined items
	for(let i in (data.T0事项 !== undefined)?data.T0事项:[]) {
		issueIndex++
		// 2020-10-25 23:30 sheyifan Issue: add existing row to the end of table
		// 2020-10-31 19:19 sheyifan Issue: select which table to insert
		let insertRow = table.insertRow()
		insertRow.innerHTML = tr.innerHTML
		// 2020-11-22 20:49 sheyifan Issue: add attributes to table row in order
		// to link with modal
		insertRow.setAttribute("data-toggle", "modal")
		insertRow.setAttribute("data-target", "#rud")
		// 2920-11-23 09:37 sheyifan Issue: change cursor on hovering table rows
		insertRow.setAttribute("style", "cursor: pointer")
		// 2020-10-27 10:57 sheyifan Bug: variable no declaration before use
		let issue = data.T0事项[i]

		// 2020-10-27 21:14 sheyifan Bug: add completed issue to the undone table
		// Cause: lack if... to justify whether '`state` < 1'
		if (issue.完成状态 <= 1 && issue.完成状态 >= 0) {
			insertRow.querySelector('th').innerHTML = issueIndex.toString()
			for (let [index, cell] of insertRow.querySelectorAll('td').entries()) {
				switch (index) {
					case 0:
						cell.innerHTML = "T0"
						break
					case 1:
						cell.innerHTML = issue.待办
						break
					case 2:
						cell.innerHTML = issue.事项说明
						break
					case 3:
						cell.innerHTML = issue.标签
						break
					case 4:
						cell.innerHTML = issue.风险
						break
					case 5:
						cell.innerHTML = issue.资源求助
						break
					case 6:
						cell.innerHTML = issue.完成状态
						break
					case 7:
						cell.innerHTML = issue.完成说明
						break
				}
			}
		}
	}
	// 2020-10-28 sheyifan Bug fix: handle undefined items
	for(let i in (data.T1事项 !== undefined)?data.T1事项:[]) {
		issueIndex++
		// 2020-10-25 23:30 sheyifan Issue: add existing row to the end of table
		let insertRow = table.insertRow()
		insertRow.innerHTML = tr.innerHTML
		// 2020-11-22 20:49 sheyifan Issue: add attributes to table row in order
		// to link with modal
		insertRow.setAttribute("data-toggle", "modal")
		insertRow.setAttribute("data-target", "#rud")
		// 2920-11-23 09:37 sheyifan Issue: change cursor on hovering table rows
		insertRow.setAttribute("style", "cursor: pointer")
		// 2020-10-27 10:57 sheyifan Bug: variable no declaration before use
		let issue = data.T1事项[i]

		// 2020-10-27 21:14 sheyifan Bug: add completed issue to the undone table
		// Cause: lack if... to justify whether '`state` < 1'
		if (issue.完成状态 <= 1 && issue.完成状态 >= 0) {
			insertRow.querySelector('th').innerHTML = issueIndex.toString()
			for (let [index, cell] of insertRow.querySelectorAll('td').entries()) {
				switch (index) {
					case 0:
						cell.innerHTML = "T1"
						break
					case 1:
						cell.innerHTML = issue.待办
						break
					case 2:
						cell.innerHTML = issue.事项说明
						break
					case 3:
						cell.innerHTML = issue.标签
						break
					case 4:
						cell.innerHTML = issue.风险
						break
					case 5:
						cell.innerHTML = issue.资源求助
						break
					case 6:
						cell.innerHTML = issue.完成状态
						break
					case 7:
						cell.innerHTML = issue.完成说明
						break
				}
			}
		}
	}
	// 2020-10-28 sheyifan Bug fix: handle undefined items
	for(let i in (data.T2事项 !== undefined)?data.T2事项:[]) {
		issueIndex++
		// 2020-10-25 23:30 sheyifan Issue: add existing row to the end of table
		let insertRow = table.insertRow()
		insertRow.innerHTML = tr.innerHTML
		// 2020-11-22 20:49 sheyifan Issue: add attributes to table row in order
		// to link with modal
		insertRow.setAttribute("data-toggle", "modal")
		insertRow.setAttribute("data-target", "#rud")
		// 2920-11-23 09:37 sheyifan Issue: change cursor on hovering table rows
		insertRow.setAttribute("style", "cursor: pointer")
		// 2020-10-27 10:57 sheyifan Bug: variable no declaration before use
		let issue = data.T2事项[i]

		// 2020-10-27 21:14 sheyifan Bug: add completed issue to the undone table
		// Cause: lack if... to justify whether '`state` < 1'
		if (issue.完成状态 <= 1 && issue.完成状态 >= 0) {
			insertRow.querySelector('th').innerHTML = issueIndex.toString()
			for (let [index, cell] of insertRow.querySelectorAll('td').entries()) {
				switch (index) {
					case 0:
						cell.innerHTML = "T2"
						break
					case 1:
						cell.innerHTML = issue.待办
						break
					case 2:
						cell.innerHTML = issue.事项说明
						break
					case 3:
						cell.innerHTML = issue.标签
						break
					case 4:
						cell.innerHTML = issue.风险
						break
					case 5:
						cell.innerHTML = issue.资源求助
						break
					case 6:
						cell.innerHTML = issue.完成状态
						break
					case 7:
						cell.innerHTML = issue.完成说明
						break
				}
			}
		}
	}
}

/**
 * 2020-10-29 sheyifan Issue: fill related data into dashboard
 * @param {{
 *     T0: FinishData,
 *     T1: FinishData,
 *     T2: FinishData
 * }} ratioData 
 * @param {Document} document
 */
function fillDashboard(ratioData, document) {
	/** @type {HTMLDivElement} */
	let box = document.getElementById("issue")
	/** @type {HTMLDivElement} */
	let box0 = document.getElementById("T0-issue")
	/** @type {HTMLDivElement} */
	let box1 = document.getElementById("T1-issue")
	/** @type {HTMLDivElement} */
	let box2 = document.getElementById("T2-issue")
	/** @type {HTMLSpanElement} */
	let tbdBadge = document.getElementById("tbd-badge")

	box.innerHTML = (ratioData.T0.finish + ratioData.T1.finish + ratioData.T2.finish).toString() + '/' +
					(ratioData.T0.finish + ratioData.T1.finish + ratioData.T2.finish + ratioData.T0.undone + ratioData.T1.undone + ratioData.T2.undone).toString()
	box0.innerHTML = ratioData.T0.finish.toString() + '/' + (ratioData.T0.undone + ratioData.T0.finish).toString()
	box1.innerHTML = ratioData.T1.finish.toString() + '/' + (ratioData.T1.finish + ratioData.T1.undone).toString()
	box2.innerHTML = ratioData.T2.finish.toString() + '/' + (ratioData.T2.finish + ratioData.T2.undone).toString()

	// count of issues to be done
	let tbdCount = ratioData.T0.undone
	if (tbdCount > 99) {
		tbdCount = 99
	}
	tbdBadge.innerHTML = tbdCount.toString()
}

/**
 * 2020-10-29 sheyifan Issue: fill badge dropdown menu with issue data today
 * @param {DayIssue} data 
 * @param {Document} document 
 */
function fillBadgeDropdownMenu(data, document) {
	/** @type {HTMLDivElement} */
	let badgeDropdownMenu = document.querySelector("#navbarDropdownMenuLink2")

	badgeDropdownMenu.addEventListener('click', (MouseEvent) => {
		/** @type {NodeListOf<HTMLAnchorElement>} */
		let badgeDropdownMenuItems = document.querySelectorAll(".dropdown-item")

		if (data.T0事项.length <= 5) {
			badgeDropdownMenuItems.item(5).style.display = "none"
			let i = 0
			for (i = 0 ; i < data.T0事项.length ; i++) {
				badgeDropdownMenuItems.item(i).querySelector('span').innerHTML = (data.T0事项[i].完成状态 * 100).toFixed(0).toString() + '%'
				if ((data.T0事项)[i].待办.length > 10) {
					badgeDropdownMenuItems.item(i).querySelector('strong').innerHTML = (data.T0事项)[i].待办.substr(0, 10) + "..."
				}
				else {
					badgeDropdownMenuItems.item(i).querySelector('strong').innerHTML = (data.T0事项)[i].待办
				}
			}
			while (i !== 5) {
				badgeDropdownMenuItems.item(i).style.display = "none"
				i++
			}
		}
		else {
			let i = 0
			for (i = 0 ; i < 5 ; i++) {
				badgeDropdownMenuItems.item(i).querySelector('span').innerHTML = (data.T0事项[i].完成状态 * 100).toFixed(0).toString() + '%'
				if ((data.T0事项)[i].待办.length > 10) {
					badgeDropdownMenuItems.item(i).querySelector('strong').innerHTML = (data.T0事项)[i].待办.substr(0, 10) + "..."
				}
				else {
					badgeDropdownMenuItems.item(i).querySelector('strong').innerHTML = (data.T0事项)[i].待办
				}
			}
		}
	})
}

/**
 * 2020-10-30 sheyifan Issue: fill chart data by invoke javascript in render thread
 * @param {Electron.webContents} webContent
 * @param {{
 *     days14: {
 *         data: Array<FinishData>
 *     },
 *     month6: {
 *         data: Array<FinishData>
 *     },
 *     thisWeek: {
 *         data: Array<number>
 *     }
 * }} data - data to be filled into chart
 * @param {Date} date
 */
function fillChart(webContents, data, date) {
	// 2020-10-27 11:28 sheyifan Issue: execute javascript in render thread by concat script
	// 2020-10-30 17:11 sheyifan Issue: initial value
	let monthData = {
		完成: [0, 0, 0, 0, 0, 50],
		未完成: [0, 0, 0, 0, 0, 10],
		labels: []
	}
	/** @type {string[]} */
	let weeklyData = {
		七日完成率: [0, 0, 0, 0, 0, 0, 0],
		七日未完成率: [0, 0, 0, 0, 0, 0, 0],
		labels: []
	}
	let dailyData = {
		完成: [65, 59, 80, 81, 56, 55, 40],
		未完成: [35, 40, 60, 47, 88, 27, 30],
		labels: []
	}

	let dateCopy0 = new Date(date)
	for (let i = 0 ; i < 6 ; i++) {
		let currentMonth = dateCopy0.getMonth() + 1
		if (currentMonth <= 0) {
			currentMonth += 12
		}
		monthData.labels.unshift(currentMonth + "月")
		monthData.完成[5 - i] = data.month6.data[i].finish
		monthData.未完成[5 - i] = data.month6.data[i].undone
		
		currentMonth--
	}

	let dateCopy1 = new Date(date)
	for (let i = 0 ; i < 7 ; i++) {
		dailyData.labels.unshift(dateCopy1.getFullYear().toString() + " " + (dateCopy1.getMonth() + 1).toString().padStart(2, '0') + "-" + dateCopy1.getDate().toString().padStart(2, '0'))
		dailyData.完成[6 - i] = data.days14.data[i].finish
		dailyData.未完成[6 - i] = data.days14.data[i].undone

		dateCopy1.setDate(dateCopy1.getDate() - 1)
	}

	let dateCopy2 = new Date(date)
	for (let i = 0 ; i < 14 ; i++) {
		if (i >= 0 && i < 7) {
			let day = ""
			switch (dateCopy2.getDay()) {
				case 0:
					day = "星期日"
					break
				case 1:
					day = "星期一"
					break
				case 2:
					day = "星期二"
					break
				case 3:
					day = "星期三"
					break
				case 4:
					day = "星期四"
					break
				case 5:
					day = "星期五"
					break
				case 6:
					day = "星期六"
					break
			}
			weeklyData.labels.unshift(day)
			if ((data.days14.data[i].finish + data.days14.data[i].undone) !== 0) {
				weeklyData.七日完成率[6 - i] = Math.floor((data.days14.data[i].finish / data.days14.data[i].undone) * 100)
				weeklyData.七日未完成率[6 - i] = 100 - weeklyData.七日完成率[6 - i]
			}
			else {
				weeklyData.七日完成率[6 - i] = 0
				weeklyData.七日未完成率[6 - i] = 0
			}
			dateCopy2.setDate(dateCopy2.getDate() - 1)
		}
	}

	// 2020-10-30 23:50 Question: fail to pass two parameter to script string in the following
	webContents.executeJavaScript("fillMonthsData(" + JSON.stringify(monthData) + ");" +
								  "fillWeeklyData(" + JSON.stringify(weeklyData) + ");" +
								  "fillDailyData(" + JSON.stringify(dailyData) + ")")
}

/**
 * 2020-11-18 16:51 sheyifan Issue: normalize data to avoid `undefined` or `null`
 * @param {DayIssue} data 
 */
function normalize(data) {
	if (data.T0事项 == undefined || data.T0事项 == null) {
		data.T0事项 = []
	}
	if (data.T1事项 == undefined || data.T1事项 == null) {
		data.T1事项 = []
	}
	if (data.T2事项 == undefined || data.T2事项 == null) {
		data.T2事项 = []
	}
	if (data.预约 == undefined || data.预约 == null) {
		data.预约 = {
			T0事项: [],
			T1事项: [],
			T2事项: []
		}
	}
	if (data.预约.T0事项 == undefined || data.预约.T0事项 == null) {
		data.预约.T0事项 = []
	}
	if (data.预约.T1事项 == undefined || data.预约.T1事项 == null) {
		data.预约.T1事项 = []
	}
	if (data.预约.T2事项 == undefined || data.预约.T2事项 == null) {
		data.预约.T2事项 = []
	}

	return data
}

/**
 * 2020-11-25 sheyifan Issue: remove issue in corresponding data source file
 * @param {{
 *     日期: string,
 *     待办: string,
 *     事项说明: string,
 *     优先级: "T0" | "T1" | "T2",
 * }} issueInfo
 */
function deleteIssue(issueInfo) {
	let date = new Date(issueInfo.日期)
	let filepath = path.resolve('./data/log', date.getFullYear().toString(), (date.getMonth() + 1).toString(), date.getDate().toString() + '.json')
	if (!fs.existsSync(filepath)) {
		console.log(`Can not find ${filepath} to remove issue.`)
		return
	}

	/** @type {DayIssue} */
	let data = JSON.parse(fs.readFileSync(filepath, { encoding: 'utf-8' }).toString())

	for (let i = data.T0事项.length - 1 ; i >= 0 ; i--) {
		if (data.T0事项[i].待办 === issueInfo.待办 && data.T0事项[i].事项说明 === issueInfo.事项说明) {
			data.T0事项.splice(i, 1)
		}
	}
	for (let i = data.T1事项.length - 1 ; i >= 0 ; i--) {
		if (data.T1事项[i].待办 === issueInfo.待办 && data.T1事项[i].事项说明 === issueInfo.事项说明) {
			data.T1事项.splice(i, 1)
		}
	}
	for (let i = data.T2事项.length - 1 ; i >= 0 ; i--) {
		if (data.T2事项[i].待办 === issueInfo.待办 && data.T2事项[i].事项说明 === issueInfo.事项说明) {
			data.T2事项.splice(i, 1)
		}
	}
	for (let i = data.预约.T0事项.length - 1 ; i >= 0 ; i--) {
		if (data.预约.T0事项[i].待办 === issueInfo.待办 && data.预约.T0事项[i].事项说明 === issueInfo.事项说明) {
			data.预约.T0事项.splice(i, 1)
		}
	}
	for (let i = data.预约.T1事项.length - 1 ; i >= 0 ; i--) {
		if (data.预约.T1事项[i].待办 === issueInfo.待办 && data.预约.T1事项[i].事项说明 === issueInfo.事项说明) {
			data.预约.T1事项.splice(i, 1)
		}
	}
	for (let i = data.预约.T2事项.length - 1 ; i >= 0 ; i--) {
		if (data.预约.T2事项[i].待办 === issueInfo.待办 && data.预约.T2事项[i].事项说明 === issueInfo.事项说明) {
			data.预约.T2事项.splice(i, 1)
		}
	}

	// 2020-10-26 20:42 sheyifan Issue: write data to file (create file if necessary)
	fs.writeFileSync(filepath, JSON.stringify(data, null, 4), 'utf-8')
	// 2020-10-28 00:55 sheyifan Issue: no need to close file after invoke writeFileSync()
}

/**
 * 2020-11-25 sheyifan Issue: remove issue in corresponding data source file
 * @param {{
 *     日期: string,
 *     待办: string,
 *     事项说明: string
 * }} issueInfo
 * @param {IssueItems} updatedIssue
 */
function updateIssue(issueInfo, updatedIssue) {
	let date = new Date(issueInfo.日期)
	let filepath = path.resolve('./data/log', date.getFullYear().toString(), (date.getMonth() + 1).toString(), date.getDate().toString() + '.json')
	if (!fs.existsSync(filepath)) {
		console.log(`Can not find ${filepath} to remove issue.`)
		return
	}

	/** @type {DayIssue} */
	let data = JSON.parse(fs.readFileSync(filepath, { encoding: 'utf-8' }).toString())

	for (let i = data.T0事项.length - 1 ; i >= 0 ; i--) {
		// 2020-11-26 sheyifan Issue: find matched issue in data source
		if (data.T0事项[i].待办 === issueInfo.待办 && data.T0事项[i].事项说明 === issueInfo.事项说明) {
			if (updatedIssue.优先级 === "T0") {
				data.T0事项[i] = updatedIssue
			}
			else {
				switch (updatedIssue.优先级) {
					case "T1":
						data.T0事项.splice(i, 1)
						data.T1事项.push(updatedIssue)
						break
					case "T2":
						data.T0事项.splice(i, 1)
						data.T2事项.push(updatedIssue)
				}
			}
		}
	}
	for (let i = data.T1事项.length - 1 ; i >= 0 ; i--) {
		// 2020-11-26 sheyifan Issue: find matched issue in data source
		if (data.T1事项[i].待办 === issueInfo.待办 && data.T1事项[i].事项说明 === issueInfo.事项说明) {
			// 2020-11-26 sheyifan Attention: priority stay unchanged
			if (updatedIssue.优先级 === "T1") {
				data.T1事项[i] = updatedIssue
			}
			else {
				switch (updatedIssue.优先级) {
					case "T0":
						data.T1事项.splice(i, 1)
						data.T0事项.push(updatedIssue)
						break
					case "T2":
						data.T1事项.splice(i, 1)
						data.T2事项.push(updatedIssue)
				}
			}
		}
	}
	for (let i = data.T2事项.length - 1 ; i >= 0 ; i--) {
		// 2020-11-26 sheyifan Issue: find matched issue in data source
		if (data.T2事项[i].待办 === issueInfo.待办 && data.T2事项[i].事项说明 === issueInfo.事项说明) {
			// 2020-11-26 sheyifan Attention: priority stay unchanged
			if (updatedIssue.优先级 === "T2") {
				data.T2事项[i] = updatedIssue
			}
			else {
				switch (updatedIssue.优先级) {
					case "T0":
						data.T2事项.splice(i, 1)
						data.T0事项.push(updatedIssue)
						break
					case "T1":
						data.T2事项.splice(i, 1)
						data.T1事项.push(updatedIssue)
				}
			}
		}
	}
	for (let i = data.预约.T0事项.length - 1 ; i >= 0 ; i--) {
		// 2020-11-26 sheyifan Issue: find matched issue in data source
		if (data.预约.T0事项[i].待办 === issueInfo.待办 && data.预约.T0事项[i].事项说明 === issueInfo.事项说明) {
			// 2020-11-26 sheyifan Attention: priority stay unchanged
			if (updatedIssue.优先级 === "T0") {
				data.预约.T0事项[i] = updatedIssue
			}
			else {
				switch (updatedIssue.优先级) {
					case "T1":
						data.预约.T0事项.splice(i, 1)
						data.预约.T1事项.push(updatedIssue)
						break
					case "T2":
						data.预约.T0事项.splice(i, 1)
						data.预约.T2事项.push(updatedIssue)
				}
			}
		}
	}
	for (let i = data.预约.T1事项.length - 1 ; i >= 0 ; i--) {
		// 2020-11-26 sheyifan Issue: find matched issue in data source
		if (data.预约.T1事项[i].待办 === issueInfo.待办 && data.预约.T1事项[i].事项说明 === issueInfo.事项说明) {
			// 2020-11-26 sheyifan Attention: priority stay unchanged
			if (updatedIssue.优先级 === "T1") {
				data.预约.T1事项[i] = updatedIssue
			}
			else {
				switch (updatedIssue.优先级) {
					case "T0":
						data.预约.T1事项.splice(i, 1)
						data.预约.T0事项.push(updatedIssue)
						break
					case "T2":
						data.预约.T1事项.splice(i, 1)
						data.预约.T2事项.push(updatedIssue)
				}
			}
		}
	}
	for (let i = data.预约.T2事项.length - 1 ; i >= 0 ; i--) {
		// 2020-11-26 sheyifan Issue: find matched issue in data source
		if (data.预约.T2事项[i].待办 === issueInfo.待办 && data.预约.T2事项[i].事项说明 === issueInfo.事项说明) {
			// 2020-11-26 sheyifan Attention: priority stay unchanged
			if (updatedIssue.优先级 === "T2") {
				data.预约.T2事项[i] = updatedIssue
			}
			else {
				switch (updatedIssue.优先级) {
					case "T0":
						data.预约.T2事项.splice(i, 1)
						data.预约.T0事项.push(updatedIssue)
						break
					case "T1":
						data.预约.T2事项.splice(i, 1)
						data.预约.T1事项.push(updatedIssue)
				}
			}
		}
	}

	// 2020-10-26 20:42 sheyifan Issue: write data to file (create file if necessary)
	fs.writeFileSync(filepath, JSON.stringify(data, null, 4), 'utf-8')
	// 2020-10-28 00:55 sheyifan Issue: no need to close file after invoke writeFileSync()
}

/**
 * 2020-12-02 sheyifan Issue: add new issue
 * @param {IssueItems} issueInfo
 * @param {'now' | 'appointed'} when
 */
function createIssue(issueInfo, when) {
	let date = new Date(issueInfo.日期)
	let filepath = path.resolve('./data/log', date.getFullYear().toString(), (date.getMonth() + 1).toString(), date.getDate().toString() + '.json')
	if (!fs.existsSync(filepath)) {
		console.log(`Can not find ${filepath} to create new  issue.`)
		return
	}

	/** @type {DayIssue} */
	let data = JSON.parse(fs.readFileSync(filepath, { encoding: 'utf-8' }).toString())

	if (when === 'now') {
		switch (issueInfo.优先级) {
			case "T0":
				data.T0事项.push(issueInfo)
				break
			case "T1":
				data.T1事项.push(issueInfo)
				break
			case "T2":
				data.T2事项.push(issueInfo)
				break
		}
	}
	else if (when === 'appointed') {
		switch (issueInfo.优先级) {
			case "T0":
				data.预约.T0事项.push(issueInfo)
				break
			case "T1":
				data.预约.T1事项.push(issueInfo)
				break
			case "T2":
				data.预约.T2事项.push(issueInfo)
				break
		}
	}
	else {
		console.log(`Invalid position to create issue. Please choose from "now" and "appointed"`)
	}

	// 2020-10-26 20:42 sheyifan Issue: write data to file (create file if necessary)
	fs.writeFileSync(filepath, JSON.stringify(data, null, 4), 'utf-8')
	// 2020-10-28 00:55 sheyifan Issue: no need to close file after invoke writeFileSync()
}

module.exports = {
	getRadio,
	fillTable,
	fillDashboard,
	fillBadgeDropdownMenu,
	fillChart,
	normalize,
	deleteIssue,
	updateIssue,
	createIssue
}